package com.cysoft.starter.repeat.core.helper;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import com.github.benmanes.caffeine.cache.LoadingCache;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

/**
 * @author 玖零
 * @version V0.0.1
 * @project cysoft-spring-boot-starter-repeat
 * @package com.cysoft.starter.repeat.core.helper
 * @date 2025-03-03 01:20
 * @desc
 */
public class CaffeineCacheHelper {

    /**
     * 创建一个 Caffeine 缓存实例
     *
     * @param defaultExpireMinutes 默认过期时间（分钟）
     * @param <K>                  缓存键的类型
     * @param <V>                  缓存值的类型
     * @return 加载型缓存实例
     */
    public static <K, V> LoadingCache<K, V> createCache(int defaultExpireMinutes) {
        return Caffeine.newBuilder()
                .expireAfter(new Expiry<K, V>() {
                    @Override
                    public long expireAfterCreate(K key, V value, long currentTime) {
                        return TimeUnit.MINUTES.toNanos(defaultExpireMinutes);
                    }

                    @Override
                    public long expireAfterUpdate(K key, V value, long currentTime, long currentDuration) {
                        return currentDuration;
                    }

                    @Override
                    public long expireAfterRead(K key, V value, long currentTime, long currentDuration) {
                        return currentDuration;
                    }
                })
                .build(key -> null);
    }

    /**
     * 动态设置缓存项的过期时间
     *
     * @param cache      缓存实例
     * @param key        缓存键
     * @param expireTime 过期时间
     * @param timeUnit   时间单位
     * @param <K>        缓存键的类型
     * @param <V>        缓存值的类型
     */
    public static <K, V> void setExpiry(LoadingCache<K, V> cache, K key, long expireTime, TimeUnit timeUnit) {
        ChronoUnit chronoUnit = convertToChronoUnit(timeUnit);
        Duration expireDuration = Duration.of(expireTime, chronoUnit);
        cache.policy().expireVariably().ifPresent(policy -> {
            policy.setExpiresAfter(key, expireDuration);
        });
    }

    /**
     * 将 TimeUnit 转换为 ChronoUnit
     *
     * @param timeUnit 要转换的 TimeUnit
     * @return 对应的 ChronoUnit
     */
    private static ChronoUnit convertToChronoUnit(TimeUnit timeUnit) {
        switch (timeUnit) {
            case NANOSECONDS:
                return ChronoUnit.NANOS;
            case MICROSECONDS:
                return ChronoUnit.MICROS;
            case MILLISECONDS:
                return ChronoUnit.MILLIS;
            case SECONDS:
                return ChronoUnit.SECONDS;
            case MINUTES:
                return ChronoUnit.MINUTES;
            case HOURS:
                return ChronoUnit.HOURS;
            case DAYS:
                return ChronoUnit.DAYS;
            default:
                throw new IllegalArgumentException("Unsupported TimeUnit: " + timeUnit);
        }
    }
}
